Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Commit 6cf8226f875784d474b884631088c4d052600a45


Parents : 13a95be
Author : Mark Qvist <mark@unsigned.io>
Date : 2025-11-27T01:35:38+01:00

Improved recording

Changes

2 files changed, 42 insertions(+), 9 deletions(-)

M LXST/Sources.py +33 -7

Diff

diff --git a/LXST/Primitives/Recorders.py b/LXST/Primitives/Recorders.py
index 42aea82..b4c8d20 100644
--- a/LXST/Primitives/Recorders.py
+++ b/LXST/Primitives/Recorders.py
@@ -4,15 +4,21 @@ import time
import os
from LXST.Sources import LineSource
from LXST.Sinks import OpusFileSink
+from LXST.Filters import BandPass, AGC
class FileRecorder():
- def __init__(self, path=None, device=None, profile=LXST.Codecs.Opus.PROFILE_AUDIO_MAX):
+ def __init__(self, path=None, device=None, profile=LXST.Codecs.Opus.PROFILE_AUDIO_MAX,
+ gain=0.0, ease_in=0.125, skip=0.075, filters=[BandPass(25, 24000)]):
self._file_path = path
self._record_device = device
self.__profile = profile
self.__source = None
self.__sink = OpusFileSink(path=self._file_path, profile=profile)
self.__null = LXST.Codecs.Null()
+ self.__filters = filters
+ self.__ease_in = ease_in
+ self.__skip = skip
+ self.__gain = gain
self.set_source(device)
@property
@@ -25,7 +31,8 @@ class FileRecorder():
def set_source(self, device=None):
self._record_device = device
- self.__source = LineSource(preferred_device=self._record_device, target_frame_ms=60, codec=self.__null, sink=self.__sink)
+ self.__source = LineSource(preferred_device=self._record_device, target_frame_ms=20, codec=self.__null, sink=self.__sink,
+ gain=self.__gain, ease_in=self.__ease_in, skip=self.__skip, filters=self.__filters)
self.__sink.source = self.__source
def set_output_path(self, path):

diff --git a/LXST/Sources.py b/LXST/Sources.py
index bac1240..7e894f2 100644
--- a/LXST/Sources.py
+++ b/LXST/Sources.py
@@ -160,7 +160,10 @@ class LineSource(LocalSource):
MAX_FRAMES = 128
DEFAULT_FRAME_MS = 80
- def __init__(self, preferred_device=None, target_frame_ms=DEFAULT_FRAME_MS, codec=None, sink=None, filters=None):
+ @staticmethod
+ def linear_gain(gain_db): return 10**(gain_db/10)
+
+ def __init__(self, preferred_device=None, target_frame_ms=DEFAULT_FRAME_MS, codec=None, sink=None, filters=None, gain=0.0, ease_in=0.0, skip=0.0):
self.preferred_device = preferred_device
self.frame_deque = deque(maxlen=self.MAX_FRAMES)
self.target_frame_ms = target_frame_ms
@@ -174,11 +177,18 @@ class LineSource(LocalSource):
self.codec = codec
self.sink = sink
self.filters = None
+ self.ease_in = ease_in
+ self.gain = gain
+ self.__skip = skip
+ self.__gain = self.linear_gain(self.gain)
+ self.__target_gain = self.__gain
if filters != None:
if type(filters) == list: self.filters = filters
else: self.filters = [filters]
+ if self.ease_in != 0.0: self.__gain = 0.0
+
@property
def codec(self): return self._codec
@@ -229,14 +239,30 @@ class LineSource(LocalSource):
else: backend_samples_per_frame = None
with self.backend.get_recorder(samples_per_frame=backend_samples_per_frame) as recorder:
+ started = time.time()
+ ease_in_completed = True if self.ease_in <= 0.0 else False
+ skip_completed = True if self.__skip <= 0.0 else False
while self.should_run:
frame_samples = recorder.record(numframes=self.samples_per_frame)
- if self.filters != None:
- for f in self.filters: frame_samples = f.handle_frame(frame_samples, self.samplerate)
- if self.codec:
- frame = self.codec.encode(frame_samples)
- if self.sink and self.sink.can_receive(from_source=self):
- self.sink.handle_frame(frame, self)
+ if not skip_completed:
+ if time.time()-started > self.__skip:
+ skip_completed = True
+ started = time.time()
+ else:
+ if self.filters != None:
+ for f in self.filters: frame_samples = f.handle_frame(frame_samples, self.samplerate)
+ if self.__gain != 1.0: frame_samples *= self.__gain
+ if self.codec:
+ frame = self.codec.encode(frame_samples)
+ if self.sink and self.sink.can_receive(from_source=self):
+ self.sink.handle_frame(frame, self)
+ if not ease_in_completed:
+ d = time.time()-started
+ self.__gain = (d/self.ease_in)*self.__target_gain
+ if self.__gain >= self.__target_gain:
+ self.__gain = self.__target_gain
+ ease_in_completed = True
+
class OpusFileSource(LocalSource):
MAX_FRAMES = 128


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────